package com.example;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 我们使用 thenCompose()和 thenCombine() 把两个 CompletableFuture 组合在一起。
 * 现在如果你想组合任意数量的 CompletableFuture，应该怎么做？
 * 我们可以使用以下两个方法组合任意数量的 CompletableFuture。
 * CompletableFuture.allOf()、CompletableFuture.anyOf()
 */
public class Demo04Test {

    /**
     * CompletableFuture.allOf() CompletableFuture.allOf的 使用场景是当你一个列表的独立future，并且你想在它们都完成后并行的做一些事情。
     *
     * 假设你想下载一个网站的 100 个不同的页面。 你可以串行的做这个操作，但是这非常消耗时间。
     * 因此你想写一个函数，传入一个页面链接，返回一个 CompletableFuture，异步的下载页面内容。
     */
    @Test
    public void test01() throws Exception {
        List<String> pageLinks = Arrays.asList("111","222","123");
        /*
        现在，当所有的页面已经下载完毕，你想计算包含关键字 '1' 页面的数量。可以使用 CompletableFuture.allOf() 达成目的。
         */
        List<CompletableFuture<String>> pageContentFutures = pageLinks.stream().map(this::downloadWebPage).collect(Collectors.toList());
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(pageContentFutures.toArray(new CompletableFuture[0]));
        /*
        使用 CompletableFuture.allOf() 的问题是它返回的是 CompletableFuture。但是我们可以通过写一些额外的代码来获取所有封装的 CompletableFuture 结果。
         */
        CompletableFuture<List<String>> allPageContentsFuture = allFutures.thenApply(
                v -> pageContentFutures.stream().map(pageContentFuture -> {
                    String pageContent = "";
                    try {
                        pageContent = pageContentFuture.get();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return pageContent;
                }).collect(Collectors.toList())
        );
        /*
        现在让我们计算包含关键字页面的数量。
         */
        CompletableFuture<Long> countFuture = allPageContentsFuture.thenApply(
                allPageContents -> allPageContents.stream().filter(pageContent -> pageContent.contains("1")).count());

        System.out.println("Number of Web Pages having '1' keyword - " + countFuture.get());
    }

    CompletableFuture<String> downloadWebPage(String pageLink) {
        return CompletableFuture.supplyAsync(() -> "downloadWebPage " + pageLink);
    }

    /**
     * CompletableFuture.anyOf()和其名字介绍的一样，当任何一个 CompletableFuture 完成的时候，返回一个新的 CompletableFuture。
     *
     * 在以下示例中，当三个中的任何一个 CompletableFuture 完成， anyOfFuture 就会完成。
     * 因为 future2 的休眠时间最少，因此她最先完成，最终的结果将是 future2 的结果。
     * CompletableFuture.anyOf() 传入一个 Future 可变参数，返回 CompletableFuture。
     * CompletableFuture.anyOf() 的问题是如果传入的 Future 可变参数返回的结果是不同类型的，这时候就需要根据传入的 Future 可变参数的结果类型做判断了。
     */
    @Test
    public void test02() throws Exception {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Result of Future 1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Result of Future 2";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Result of Future 3";
        });
        CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println(anyOfFuture.get());
    }

}
